#include "ScannerImpl.h"
#include "Buffer.h"
#include "AutomatVar1.h"
#include <assert.h>
#include <cstdlib>
#include "SymtableV0.h"
#include "Test.h"

ScannerImpl::ScannerImpl(const char * const srcFile /*= NULL*/, const char * const dstFile) :
    automat(NULL),//see below
    buffer(new Buffer(srcFile, dstFile)),
    symtable(new SymtableV0()),
    lexemFrac(new StringTabV0()),
    isTokenFound(false),
    isStop(false),
    token(NULL),
    scanned_row(1),//1-based numbering
    scanned_col(0),
    scanned_integer_len(0)
{
    automat = new AutomatVar1(this);
    scanned_integer[0] = '\0'; 
}

ScannerImpl::~ScannerImpl(void) {
    delete automat;
    delete buffer;
    delete symtable;
    delete lexemFrac;
    delete token; //CLGR maybe the caller deletes all tokens (including this token)
}

Token* ScannerImpl::nextToken() {
    if (isStop)
        return NULL; //case: a further call nextToken() returned the last token and stopped

    isTokenFound = false;
    isStop = false;
    token = NULL;
    int c = 0;

    while (!isTokenFound && !isStop) {
        //trigger automat
        c = buffer->getchar(); //if c == EOF symtable and automat handles it correctly

        //determine the token data (row and column)
        if(c == '\n'){ 
            scanned_col = 0;
            scanned_row++; 
        }
        else
        {
            scanned_col++; 
        }

        automat->readChar(c);
    }

    return token; //returns null, if isStop (in e.g. EOF case)
}

void ScannerImpl::freeToken(Token token) {
}

void ScannerImpl::mkToken(TType type, long long value, unsigned int row, unsigned int col) {
    TokenData tData;
    if (type == TOK_IDENTIFIER) {
        tData.asKey = symtable->mkToken(type);
    } else if (type == TOK_INTEGER) {
        scanned_integer[scanned_integer_len] = '\0'; 
        tData.asInt = atoll(scanned_integer);
        if(scanned_integer_len == SCANNED_INT_MAX_LENGTH){
            printf("###WARNING: Number probably to long\n");
        }
        scanned_integer[0] = '\0';
        scanned_integer_len = 0;
    } else if (type == TOK_SPECIAL) {
        tData.asChar = 0; //data is unused/invalid
    } else if (type == TOK_COMMENT_OPEN) {
        tData.asChar = 0; //data is unused/invalid
    } else if (type == TOK_COMMENT_CLOSE) {
        tData.asChar = 0; //data is unused/invalid
    } else {//type is a sign
        tData.asChar = (char) value;
    }
    token = new Token(type, row, col, tData);
    isTokenFound = true;
}

void ScannerImpl::ungetChar(int times) {
    scanned_col -= times;
    buffer->ungetchar(times);
}

Information ScannerImpl::lookup(Key key) {
    return symtable->lookup(key);
}

void ScannerImpl::stop() {
    //printf("ScannerImpl::stop()\n");
    isStop = true;
}

void ScannerImpl::putCharForInteger(char c)//from clgr
{
    if(scanned_integer_len >= SCANNED_INT_MAX_LENGTH){
        fprintf(stderr, "ERROR: INTEGER TO LONG\n");
        exit(1);
    } else {
        scanned_integer[scanned_integer_len] = c;
        scanned_integer_len++;
    }
}

void ScannerImpl::putCharForIdentifier(char c)//from clgr
{
        symtable->putChar(c);
}

unsigned int ScannerImpl::getRow()//from clgr
{
    return scanned_row;
}

unsigned int ScannerImpl::getCol()//from clgr
{
    return scanned_col;
}

SymtabEntry* ScannerImpl::getSymtabEntry(Key key)
{
    return symtable->getSymtabEntry(key);
}

#ifdef WITH_SCANNER_MAIN
int main(int argc, char ** argv) 
{
    ScannerV0* scanner = NULL;
    if(argc == 2) {//ignore output
        scanner = new ScannerImpl(argv[1], argv[2]);
    } else {
        scanner = new ScannerImpl("Test_Comment.txt", "Test.out");
    }

    Token* t = NULL;
    int tokenNr = 0;
    int counter = 0;

    while ((t = scanner->nextToken()) != NULL) {
        //for debugging:
        //printf("Token #%d\n", ++tokenNr);

        switch (t->getType()) {
        case TOK_PLUS:
            printf("Token Plus                    Line: %5d Column: %5d Lexem: +\n", t->getRow(), t->getCol());
            break;
        case TOK_MINUS:
            printf("Token Minus                   Line: %5d Column: %5d Lexem: -\n", t->getRow(), t->getCol());
            break;
        case TOK_SLASH:
            printf("Token Slash                   Line: %5d Column: %5d Lexem: /\n", t->getRow(), t->getCol());
            break;
        case TOK_STAR:
            printf("Token Star                    Line: %5d Column: %5d Lexem: *\n", t->getRow(), t->getCol());
            break;
        case TOK_ANGLE_BR_OPEN:
            printf("Token Angle Brackets Open     Line: %5d Column: %5d Lexem: <\n", t->getRow(), t->getCol());
            break;
        case TOK_ANGLE_BR_CLOSE:
            printf("Token Angle Brackets Close    Line: %5d Column: %5d Lexem: >\n", t->getRow(), t->getCol());
            break;
        case TOK_ASSIGN:
            printf("Token Assign                  Line: %5d Column: %5d Lexem: =\n", t->getRow(), t->getCol());
            break;
        case TOK_EXCL:
            printf("Token Exclamation             Line: %5d Column: %5d Lexem: !\n", t->getRow(), t->getCol());
            break;
        case TOK_AMPERSAND:
            printf("Token Ampersand               Line: %5d Column: %5d Lexem: &\n", t->getRow(), t->getCol());
            break;
        case TOK_SEMICOLON:
            printf("Token Semicolon               Line: %5d Column: %5d Lexem: ;\n", t->getRow(), t->getCol());
            break;
        case TOK_BR_OPEN:
            printf("Token Brackets Open           Line: %5d Column: %5d Lexem: (\n", t->getRow(), t->getCol());
            break;
        case TOK_BR_CLOSE:
            printf("Token Brackets Close          Line: %5d Column: %5d Lexem: )\n", t->getRow(), t->getCol());
            break;
        case TOK_CURLY_BR_OPEN:
            printf("Token Curly Brackets Open     Line: %5d Column: %5d Lexem: {\n", t->getRow(), t->getCol());
            break;
        case TOK_CURLY_BR_CLOSE:
            printf("Token Curly Brackets Close    Line: %5d Column: %5d Lexem: }\n", t->getRow(), t->getCol());
            break;
        case TOK_SQ_BR_OPEN:
            printf("Token Square Brackets Open    Line: %5d Column: %5d Lexem: [\n", t->getRow(), t->getCol());
            break;
        case TOK_SQ_BR_CLOSE:
            printf("Token Square Brackets Close   Line: %5d Column: %5d Lexem: ]\n", t->getRow(), t->getCol());
            break;


        case TOK_SPECIAL:
            printf("Token Special                 Line: %5d Column: %5d Lexem: <!>\n", t->getRow(), t->getCol());
            break;
        case TOK_INTEGER:
            printf("Token Integer                 Line: %5d Column: %5d Value: %lld\n", t->getRow(), t->getCol(), t->getInt());
            break;
        case TOK_IDENTIFIER: {
            printf("Token Identifier              Line: %5d Column: %5d Lexem: %s\n", t->getRow(), t->getCol(), scanner->lookup(t->getKey()).lexem);
            break; }
        case TOK_COMMENT_OPEN:
            printf("Token Comment Open            Line: %5d Column: %5d Lexem: /*\n", t->getRow(), t->getCol());
            break;
        case TOK_COMMENT_CLOSE:
            printf("Token Comment Close           Line: %5d Column: %5d Lexem: */\n", t->getRow(), t->getCol(), t->getChar());
            break;
        //Added in Task 2
        case TOK_COLON:
            printf("Token Colon                   Line: %5d Column: %5d Lexem: :\n", t->getRow(), t->getCol());
            break;
        case TOK_PRINT:
            printf("Token Print                   Line: %5d Column: %5d Lexem: PRINT\n", t->getRow(), t->getCol());
            break;
        case TOK_READ:
            printf("Token Read                    Line: %5d Column: %5d Lexem: READ\n", t->getRow(), t->getCol());
            break;
        case TOK_IF:
            printf("Token If                      Line: %5d Column: %5d Lexem: IF\n", t->getRow(), t->getCol());
            break;
        case TOK_ELSE:
            printf("Token Else                    Line: %5d Column: %5d Lexem: ELSE\n", t->getRow(), t->getCol());
            break;
        case TOK_WHILE:
            printf("Token While                   Line: %5d Column: %5d Lexem: WHILE\n", t->getRow(), t->getCol());
            break;
        case TOK_INT:
            printf("Token Int                     Line: %5d Column: %5d Lexem: INT\n", t->getRow(), t->getCol());
            break;


        default: //UNDEF
            break;
        }
    }

    delete scanner;
    return 0;
}
#endif
